import { omit } from 'lodash-es'
import type {
  Expression, Literal, Ast, Node, Identifier,
} from '@/types/retrieval-sql'
import { expendIdentifiers } from '@/utils/retrieval-sql/core/config'

function walkSelf(node: Node): Node {
  if (node.type === 'Expression' && node.right?.type === 'Expression' && node.right?.variant === 'list') {
    return node.right.expression!
      .reduce<Expression | null>((res, cv) => {
        const exp: Expression = {
          type: 'Expression',
          operation: (node as Expression)!.operation,
          variant: 'operation',
          left: node.left,
          right: cv,
          start: node.left!.start,
          end: cv.end,
        }

        if (res === null) return exp

        return <Expression>{
          type: 'Expression',
          operation: (node.right as Expression)!.operation,
          variant: 'operation',
          left: res,
          right: exp,
          start: res.start,
          end: exp.end,
        }
      }, null)!
  }
  return node
}
function walk(node: Node, parent: Expression | null): any {
  node = walkSelf(node) as Node
  const type = node.type.toLowerCase()

  if (node.type === 'Expression') {
    if (node.operation === 'BETWEEN') {
      const express = node as Expression<Literal, Literal>
      return walk({
        type: express.left!.type.toLowerCase(),
        variant: express.left!.variant,
        value: `${express.left!.value}:${express.right!.value}`,
      } as any, parent)
    }

    const name = (node.left! as Identifier).name || ''
    const expend = expendIdentifiers.find((it) => it.key === name)
    if (expend && expend.keys) {
      const res = expend.keys.slice(0, Number(name[1]))
        .map((name) => ({
          type: 'expression' as any,
          operation: '=',
          variant: 'operation',
          left: {
            type: 'Identifier',
            variant: 'column',
            name,
          },
          right: {
            type: 'Literal',
            variant: 'text',
            value: ((node as Expression).right! as Literal).value,
          },
        }))
        .reduce<Expression | null>((res, cv) => {
          if (res === null) {
            return cv
          }

          return {
            type: 'expression',
            operation: 'or',
            left: res,
            right: cv,
          } as any
        }, null)
      return walk(res!, parent)
    }
  }
  return {
    ...omit(node, ['start', 'end']),
    type,
    operation: (node as any).operation?.toLowerCase(),
    left: (node as Expression).left ? walk((node as any).left, node as Expression) : undefined,
    right: (node as Expression).right ? walk((node as any).right, node as Expression) : undefined,
  }
}

export default function conditionGenerator(ast: Ast) {
  return walk(ast, null)
}
